/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xenon3d.vector;

import java.io.Serializable;

/**
 * A single precision floating point 3 by 3 rotation matrix. This matrix
 * represents a 3D rotation together with an optional uniform scaling factor.
 * @author Volker Everts
 * @version 0.1 - 18.09.2011: Created
 */
public class Matrix3f implements Serializable {

    // <editor-fold defaultstate="collapsed" desc=" Static Attributes ">

    /** The serial version UID. */
    private static final long serialVersionUID = 20110918L;

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Private Fields ">

    /** The first matrix element in the first row. */
    float m11 = 1.0f;

    /** The second matrix element in the first row. */
    float m12 = 0.0f;

    /** The third matrix element in the first row. */
    float m13 = 0.0f;

    /** The first matrix element in the second row. */
    float m21 = 0.0f;

    /** The second matrix element in the second row. */
    float m22 = 1.0f;

    /** The third matrix element in the second row. */
    float m23 = 0.0f;

    /** The first matrix element in the third row. */
    float m31 = 0.0f;

    /** The second matrix element in the third row. */
    float m32 = 0.0f;

    /** The third matrix element in the third row. */
    float m33 = 1.0f;

    /** The uniform scale factor. */
    float scale = 1.0f;

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Initialization ">

    /**
     * Creates a new Matrix3f unity matrix.
     */
    public Matrix3f() {

    }

    /**
     * Creates a new Matrix3f with the same values as the specified other
     * matrix
     * @param mat the other matrix
     */
    public Matrix3f(Matrix3f mat) {
        set(mat);
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Properties (General) ">

    /**
     * Sets the value of this Matrix3f to the value of another matrix.
     * @param mat the other matrix
     */
    public final void set(Matrix3f mat) {
        m11 = mat.m11;
        m12 = mat.m12;
        m13 = mat.m13;
        m21 = mat.m21;
        m22 = mat.m22;
        m23 = mat.m23;
        m31 = mat.m31;
        m32 = mat.m32;
        m33 = mat.m33;
        scale = mat.scale;
    }

    /**
     * Sets the values in this Matrix3f from the supplied float array, starting
     * at the specified offset. The number of data elements expected is 10. At
     * the first position in the data array is the uniform scale factor of the
     * matrix, followed by 9 data elements representing the matrix itself. The
     * matrix data elements are in column-major order.<p>
     * NOTE: it is strongly advised to check whether the matrix data really
     * represent a 3D rotation matrix with uniform scale factor. This can be
     * checked by calling isRotation().
     * @param fv the float array containing the matrix data in column-major order
     * @param offset the offset at which the data ist stored in the float array
     */
    public final void set(float[] fv, int offset) {
        scale = fv[offset];
        m11 = fv[offset + 1];
        m21 = fv[offset + 2];
        m31 = fv[offset + 3];
        m12 = fv[offset + 4];
        m22 = fv[offset + 5];
        m32 = fv[offset + 6];
        m31 = fv[offset + 7];
        m32 = fv[offset + 8];
        m33 = fv[offset + 9];
    }

    /**
     * Copies the values of this Matrix3f into the supplied float array,
     * starting at the specified offset. The number of data elements written is
     * 10. At the first position in the data array will be the uniform scale
     * factor of the matrix, followed by 9 data elements representing the matrix
     * itself. The matrix data elements are in column-major order.
     * @param fv the float array that will contain the values of the vector in column-major order
     * @param offset the offset at which the data ist to be stored in the float array
     */
    public final void get(float[] fv, int offset) {
        fv[offset + 0] = scale;
        fv[offset + 1] = m11;
        fv[offset + 2] = m21;
        fv[offset + 3] = m31;
        fv[offset + 4] = m12;
        fv[offset + 5] = m22;
        fv[offset + 6] = m32;
        fv[offset + 7] = m13;
        fv[offset + 8] = m23;
        fv[offset + 9] = m33;
    }

    /**
     * Sets the values in this Matrix3f from the supplied float array, which
     * must have a size of either 9 or 16 elements (the size of a 3x3 or 4x4
     * matrix). Any other size of the data array will result in an
     * IllegalArgumentException to be thrown. Of these data elements, only the
     * elements representing the upper left 3x3 matrix are used. The data
     * elements are expected to be in column-major order.<p>
     * NOTE: it is strongly advised to check whether the matrix data really
     * represent a 3D rotation matrix with uniform scale factor. This can be
     * checked by calling isRotation().
     * @param fv the float array containing the matrix data in column-major order
     */
    public final void set(float[] fv) {
        if (fv.length == 9) {
            m11 = fv[0];
            m21 = fv[1];
            m31 = fv[2];
            m12 = fv[3];
            m22 = fv[4];
            m32 = fv[5];
            m13 = fv[6];
            m23 = fv[7];
            m33 = fv[8];
        }
        else if (fv.length == 16) {
            m11 = fv[0];
            m21 = fv[1];
            m31 = fv[2];
            m12 = fv[4];
            m22 = fv[5];
            m32 = fv[6];
            m13 = fv[8];
            m23 = fv[9];
            m33 = fv[10];
        }
        else throw new IllegalArgumentException();
    }

    /**
     * Copies the values of this Matrix3f into the supplied float array, which
     * must have a size of either 9 or 16 elements (the size of a 3x3 or 4x4
     * matrix). Any other size of the data array will result in an
     * IllegalArgumentException to be thrown. Of these data elements, only the
     * elements representing the upper left 3 x 3 matrix are written. The data
     * elements are expected to be in column-major order.<p>
     * @param fv the float array that will contain the values of the vector in column-major order
     */
    public final void get(float[] fv) {
        if (fv.length == 9) {
            fv[0] = m11;
            fv[1] = m21;
            fv[2] = m31;
            fv[3] = m12;
            fv[4] = m22;
            fv[5] = m32;
            fv[6] = m13;
            fv[7] = m23;
            fv[8] = m33;
        }
        else if (fv.length == 16) {
            fv[0] = m11;
            fv[1] = m21;
            fv[2] = m31;
            fv[4] = m12;
            fv[5] = m22;
            fv[6] = m32;
            fv[8] = m13;
            fv[9] = m23;
            fv[10] = m33;
        }
        else throw new IllegalArgumentException();
    }

    /**
     * Computes the determinant of this matrix.
     * @return the determinant of this matrix
     */
    public final float det() {
        return
            m11 * (m22 * m33 - m23 * m32) +
            m12 * (m23 * m31 - m21 * m33) +
            m13 * (m21 * m32 - m22 * m31);
    }

    /**
     * Returns true, if this matrix represents a 3D rotation with uniform scale
     * factor. The calculation is made using epsilon equality, using the default
     * epsilon value of the FMath package.
     * @return true, if this matrix is represents a 3D rotation using epsilon equality
     */
    public final boolean isRotationScale() {
        return
                FMath.isEqual(det(), scale, FMath.EPS);
    }

    /**
     * Returns true, if this matrix represents an unscaled 3D rotation (scale = 1).
     * The calculation is made using epsilon equality, using the default epsilon
     * value of the FMath package.
     * @return true, if this matrix is represents a 3D rotation
     */
    public final boolean isRotation() {
        return
                FMath.isUnit(det(), FMath.EPS);
    }

    /**
     * Returns whether or not this matrix has a uniform scale component.
     * @return true, if there is a uniform scale component
     */
    public boolean hasScale() {
        return scale != 1.0f;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Properties (Identity and Scale) ">

    /**
     * Sets this Matrix3f to identity.
     */
    public final void setIdentity() {
        m11 = 1.0f;
        m12 = 0.0f;
        m13 = 0.0f;
        m21 = 0.0f;
        m22 = 1.0f;
        m23 = 0.0f;
        m31 = 0.0f;
        m32 = 0.0f;
        m33 = 1.0f;
        scale = 1.0f;
    }

    /**
     * Returns whether or not this matrix is the identity matrix, using exact
     * floating point math.
     * @return true, if this matrix is an exact identity matrix
     */
    public final boolean isIdentity() {
        return
                scale == 1.0f &&
                m11   == 1.0f &&
                m22   == 1.0f &&
                m33   == 1.0f &&
                det() == 1.0f;
    }

    /**
     * Returns whether or not this matrix is the identity matrix, using epsilon
     * equality.
     * @param eps the epsilon paramter, must always be non-negative
     * @return true, if this matrix is epsilon equal to the identity matrix
     */
    public final boolean isIdentity(float eps) {
        return
                FMath.isUnit(scale, eps) &&
                FMath.isUnit(m11,   eps) &&
                FMath.isUnit(m22,   eps) &&
                FMath.isUnit(m33,   eps) &&
                FMath.isUnit(det(), eps);
    }

    /**
     * Checks wheather or not this matrix is the identity matrix, using epsilon
     * equality. If this matrix is epsilon equal to the identity matrix, its
     * elements are set to reprenet the exact identity matrix.
     * @param eps the epsilon paramter, must always be non-negative
     * @return true, if this matrix is epsilon equal to the identity matrix
     */
    public final boolean checkIdentity(float eps) {
        if (isIdentity(eps)) {
            setIdentity();
            return true;
        }
        return false;
    }

    /**
     * Sets the value of this matrix to a scale matrix with the passed scale.
     * @param scale the scale factor for the matrix
     */
    public final void set(float scale) {
        m11 = scale;
        m12 = 0.0f;
        m13 = 0.0f;
        m21 = 0.0f;
        m22 = scale;
        m23 = 0.0f;
        m31 = 0.0f;
        m32 = 0.0f;
        m33 = scale;
        this.scale = scale;
    }

    /**
     * Sets the scale component of the current matrix by factoring out the
     * current scale and multiplying by the new scale.
     * @param scale the new scale
     */
    public final void setScale(float scale) {
        if (scale <= 0.0f) throw new IllegalArgumentException();
        float s = scale / this.scale;
        m11 *= s;
        m12 *= s;
        m13 *= s;
        m21 *= s;
        m22 *= s;
        m23 *= s;
        m31 *= s;
        m32 *= s;
        m33 *= s;
        this.scale = scale;
    }

    /**
     * Returns the uniform scale factor.
     * @return the uniform scale factor of this matrix
     */
    public final float getScale() {
        return scale;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Properties (Rotation) ">

    /**
     * Sets the value of this matrix to a counter clockwise rotation about the
     * x axis. Any preexisting scale is reset to 1.
     * @param angle the angle in degrees to rotate about the X axis
     */
    public final void rotX(float angle) {
        float sin = FMath.sin(angle);
        float cos = FMath.cos(angle);
        m22 = cos;
        m23 = -sin;
        m32 = sin;
        m33 = cos;
        m11 = 1.0f;
        m12 = m13 = m12 = m31 = 0.0f;
        scale = 1.0f;
    }

    /**
     * Sets the value of this matrix to a counter clockwise rotation about the
     * y axis. Any preexisting scale is reset to 1.
     * @param angle the angle to rotate about the Y axis
     */
    public final void rotY(float angle) {
        float sin = FMath.sin(angle);
        float cos = FMath.cos(angle);
        m11 = cos;
        m13 = sin;
        m31 = -sin;
        m33 = cos;
        m22 = 1.0f;
        m12 = m21 = m23 = m32 = 0.0f;
        scale = 1.0f;
    }

    /**
     * Sets the value of this matrix to a counter clockwise rotation about the
     * z axis. Any preexisting scale is reset to 1.
     * @param angle the angle to rotate about the Z axis
     */
    public final void rotZ(float angle) {
        float sin = FMath.sin(angle);
        float cos = FMath.cos(angle);
        m11 = cos;
        m12 = -sin;
        m21 = sin;
        m22 = cos;
        m33 = 1.0f;
        m13 = m23 = m31 = m32 = 0.0f;
        scale = 1.0f;
    }

    /**
     * Sets the value of this matrix to the rotation matrix converted from the
     * Euler angles provided; Any preexisting scale is reset to 1.
     * The euler rotation angles are applied first about the X, then Y then Z
     * axis. These rotations are applied using a static frame of reference.
     * In other words, the orientation of the Y rotation axis is not affected
     * by the X rotation and the orientation of the Z rotation axis is not
     * affected by the X or Y rotation.
     * @param e the EulerAngle3f
     */
    public final void set(EulerAngle3f e) {
        float cr = FMath.cos(e.x);
        float sr = FMath.sin(e.x);
        float cp = FMath.cos(e.y);
        float sp = FMath.sin(e.y);
        float cy = FMath.cos(e.z);
        float sy = FMath.sin(e.z);
        float srsp = sr * sp;
        float crsp = cr * sp;
        m11 = cp * cy;
        m12 = cp * sy;
        m13 = -sp;
        m21 = srsp * cy - cr * sy;
        m22 = srsp * sy + cr * cy;
        m23 = sr * cp;
        m31 = crsp * cy + sr * sy;
        m32 = crsp * sy - sr * cy;
        m33 = cr * cp;
        scale = 1.0f;
    }

    /**
     * Returns an euler rotation tuple that is equivalent to that set by
     * <code>setRotation(Tuple3f)</code>. Note that it does not necessarily
     * return the *same* Euler angles, but the rotation will be equivalent, i.e.
     * will have the same result when used to rotate a vector or node.
     * @param result the euler angles that are equivalent to the rotation
     * represented by this matrix.
     */
    public final void get(EulerAngle3f result) {
        result.set(this);
    }

    /**
     * Sets the value of this matrix to the matrix conversion of the (single
     * precision) axis and angle arguments. Any preexisting scale is reset to 1.
     * @param angle the angle in degrees
     * @param axis the axis vector
     */
    public final void set(Vector3f axis, float angle) {
        float mag = axis.length();
        if (mag < FMath.EPS) setIdentity();
        else {
            float ax = axis.x / mag;
            float ay = axis.y / mag;
            float az = axis.z / mag;
            float sin = FMath.sin(angle);
            float cos = FMath.cos(angle);
            float t = 1.0f - cos;
            float xz = ax * az;
            float xy = ax * ay;
            float yz = ay * az;
            m11 = t * ax * ax + cos;
            m12 = t * xy - sin * az;
            m13 = t * xz + sin * ay;
            m21 = t * xy + sin * az;
            m22 = t * ay * ay + cos;
            m23 = t * yz - sin * ax;
            m31 = t * xz - sin * ay;
            m32 = t * yz + sin * ax;
            m33 = t * az * az + cos;
            scale = 1.0f;
        }
    }

    /**
     * Gets the axis of rotation and an angle that are equivalent to the axis
     * and angle pair set by <code>setRotation(float, Vector3f)</code>. Note
     * that it does not necessarily return the *same* angle and axis, but the
     * rotation will be equivalent, i.e. will have the same result when used to
     * rotate a vector or node.
     * @param axis the axis of rotation
     * @return the angle of rotation in degrees
     */
    public final float get(Vector3f axis) {
        float x = m32 - m23;
        float y = m13 - m31;
        float z = m21 - m12;
        float angle = 0.0f;
        float mag = FMath.sqrt(x * x + y * y + z * z);
        if (mag > FMath.EPS) {
            float sin = 0.5f * mag;
            float cos = 0.5f * (m11 + m22 + m33 - 1.0f);
            axis.x = x / mag;
            axis.y = y / mag;
            axis.z = z / mag;
            angle = FMath.atan2(sin, cos);
        }
        else axis.set(0.0f, 1.0f, 0.0f);
        return angle;
    }

    /**
     * Sets the value of this matrix to the matrix conversion of the (single
     * precision) axis-angle. Any preexisting scale is reset to 1.
     * @param a the axis-angle object
     */
    public final void set(AxisAngle4f a) {
        Vector3f axis = new Vector3f();
        float angle = a.get(axis);
        set(axis, angle);
    }

    /**
     * Gets the axis-angle which is equivalent to the axis-angle set by
     * <code>setRotation(AxisAngle4f)</code>. Note that it does not necessarily
     * return the *same* axis-angle, but the rotation will be equivalent, i.e.
     * will have the same result when used to rotate a vector or node.
     * @param result the axis-angle
     */
    public final void get(AxisAngle4f result) {
        result.set(this);
    }

    /**
     * Sets the value of this matrix to the matrix conversion of the
     * (single precision) quaternion argument. Any preexisting scale is reset
     * to 1.
     * @param q the quaternion to be converted
     */
    public final void set(Quat4f q) {
        float x = q.x;
        float y = q.y;
        float z = q.z;
        float w = q.w;
        m11 = 1.0f - 2.0f * y * y - 2.0f * z * z;
        m12 = 2.0f * (x * y - w * z);
        m13 = 2.0f * (x * z + w * y);
        m21 = 2.0f * (x * y + w * z);
        m22 = 1.0f - 2.0f * x * x - 2.0f * z * z;
        m23 = 2.0f * (y * z - w * x);
        m31 = 2.0f * (x * z - w * y);
        m32 = 2.0f * (y * z + w * x);
        m33 = 1.0f - 2.0f * x * x - 2.0f * y * y;
        scale = 1.0f;
    }

    /**
     * Returns the normalized rotational component of this matrix; the values
     * are placed into the Quat4f parameter.
     * @param result quaternion into which the rotation component is placed
     */
    public final void get(Quat4f result) {
        result.set(this);
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Methods ">

    /**
     * Sets the value of this matrix to its transpose.
     */
    public final void transpose() {
        float temp;
        temp = m21;
        m21 = m12;
        m12 = temp;
        temp = m31;
        m31 = m13;
        m13 = temp;
        temp = m32;
        m32 = m23;
        m23 = temp;
    }

    /**
     * Sets the value of this matrix to the transpose of another matrix.
     * @param mat the other matrix
     */
    public final void transpose(Matrix3f mat) {
        set(mat);
        transpose();
    }

    /**
     * Inverts this matrix in place.
     */
    public final void invert() {
        transpose();
    }

    /**
     * Sets the value of this matrix to the matrix inverse of the specified
     * matrix.
     * @param mat the matrix to be inverted
     */
    public final void invert(Matrix3f mat) {
        transpose(mat);
    }

    /**
     * Sets the value of this matrix to the result of multiplying itself with
     * matrix mat.
     * @param mat the other matrix
     */
    public final void mul(Matrix3f mat) {
        mul(this, mat);
    }

    /**
     * Sets the value of this matrix to the result of multiplying the two
     * argument matrices together.
     * @param m1 the first matrix
     * @param m2 the second matrix
     */
    public final void mul(Matrix3f m1, Matrix3f m2) {
        float a1 = m1.m11 * m2.m11 + m1.m12 * m2.m21 + m1.m13 * m2.m31;
        float a2 = m1.m11 * m2.m12 + m1.m12 * m2.m22 + m1.m13 * m2.m32;
        float a3 = m1.m11 * m2.m13 + m1.m12 * m2.m23 + m1.m13 * m2.m33;
        float a4 = m1.m21 * m2.m11 + m1.m22 * m2.m21 + m1.m23 * m2.m31;
        float a5 = m1.m21 * m2.m12 + m1.m22 * m2.m22 + m1.m23 * m2.m32;
        float a6 = m1.m21 * m2.m13 + m1.m22 * m2.m23 + m1.m23 * m2.m33;
        float a7 = m1.m31 * m2.m11 + m1.m32 * m2.m21 + m1.m33 * m2.m31;
        float a8 = m1.m31 * m2.m12 + m1.m32 * m2.m22 + m1.m33 * m2.m32;
        float a9 = m1.m31 * m2.m13 + m1.m32 * m2.m23 + m1.m33 * m2.m33;
        m11 = a1;
        m12 = a2;
        m13 = a3;
        m21 = a4;
        m22 = a5;
        m23 = a6;
        m31 = a7;
        m32 = a8;
        m33 = a9;
        scale = m1.scale * m2.scale;
    }

    /**
     * Performs singular value decomposition normalization of this matrix.
     */
    public final void normalize() {
        if (scale == 1.0f) return;
        m11 /= scale;
        m12 /= scale;
        m13 /= scale;
        m21 /= scale;
        m22 /= scale;
        m23 /= scale;
        m31 /= scale;
        m32 /= scale;
        m33 /= scale;
        scale = 1.0f;
    }

    /**
     * Performs singular value decomposition normalization of another matrix mat
     * and places the normalized values into this.
     * @param mat the other matrix
     */
    public final void normalize(Matrix3f mat) {
        set(mat);
        normalize();
    }

    /**
     * Perform cross product normalization of this matrix.
     */
    public final void normalizeCP() {
        float mag = 1.0f / FMath.sqrt(m11 * m11 + m21 * m21 + m31 * m31);
        m11 *= mag;
        m21 *= mag;
        m31 *= mag;
        mag = 1.0f / FMath.sqrt(m12 * m12 + m22 * m22 + m32 * m32);
        m12 *= mag;
        m22 *= mag;
        m32 *= mag;
        m13 = m21 * m32 - m22 * m31;
        m23 = m12 * m31 - m11 * m32;
        m33 = m11 * m22 - m12 * m21;
        scale = 1.0f;
    }

    /**
     * Performa cross product normalization of another matrix mat and placea the
     * normalized values into this matrix.
     * @param mat the other matrix
     */
    public final void normalizeCP(Matrix3f mat) {
        set(mat);
        normalizeCP();
    }

    /**
     * Negates the value of this matrix: this = -this.
     */
    public final void negate() {
        m11 = -m11;
        m12 = -m12;
        m13 = -m13;
        m21 = -m21;
        m22 = -m22;
        m23 = -m23;
        m31 = -m31;
        m32 = -m32;
        m33 = -m33;
    }

    /**
     * Sets the value of this matrix equal to the negation of another Matrix3f.
     * @param mat the other matrix
     */
    public final void negate(Matrix3f mat) {
        set(mat);
        negate();
    }

    /**
     * Multiply this matrix by the vector v and place the result
     * back into the vector (v = this * v).
     * @param v the vector to be multiplied by this matrix and then replaced
     */
    public final void transform(Vector3f v) {
        transform(v, v);
    }

    /**
     * Multiply this matrix by the vector v and and place the result
     * into the vector result (result = this * v).
     * @param v the vector to be multiplied by this matrix
     * @param result the vector into which the product is placed
     */
    public final void transform(Vector3f v, Vector3f result) {
        float x = v.x;
        float y = v.y;
        float z = v.z;
        result.x = m11 * x + m12 * y + m13 * z;
        result.y = m21 * x + m22 * y + m23 * z;
        result.z = m31 * x + m32 * y + m33 * z;
    }

    /**
     * Returns true if the L-infinite distance between this matrix and another
     * matrix m1 is less than or equal to the specified positive epsilon
     * parameter. The L-infinite distance is equal to MAX[i=0,1,2 ; j=0,1,2 ;
     * abs(this.m(i,j) - mat.m(i,j)].
     * @param mat the matrix to be compared to this matrix
     * @param eps the epsilon parameter
     * @return true, if this matrix is epsilon equal to the other matrix
     */
    public final boolean equals(Matrix3f mat, float eps) {
        return
            FMath.isEqual(m11, mat.m11, eps) &&
            FMath.isEqual(m12, mat.m12, eps) &&
            FMath.isEqual(m13, mat.m13, eps) &&
            FMath.isEqual(m21, mat.m21, eps) &&
            FMath.isEqual(m22, mat.m22, eps) &&
            FMath.isEqual(m23, mat.m23, eps) &&
            FMath.isEqual(m31, mat.m31, eps) &&
            FMath.isEqual(m32, mat.m32, eps) &&
            FMath.isEqual(m33, mat.m33, eps);
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Implementation Object ">

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (obj instanceof Matrix3f) {
            Matrix3f mat = (Matrix3f) obj;
            return
                m11 == mat.m11 && m12 == mat.m12 && m13 == mat.m13 &&
                m21 == mat.m21 && m22 == mat.m22 && m23 == mat.m23 &&
                m31 == mat.m31 && m32 == mat.m32 && m33 == mat.m33;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int bits = 3;
        bits = 31 * bits + Float.floatToIntBits(m11);
        bits = 31 * bits + Float.floatToIntBits(m12);
        bits = 31 * bits + Float.floatToIntBits(m13);
        bits = 31 * bits + Float.floatToIntBits(m21);
        bits = 31 * bits + Float.floatToIntBits(m22);
        bits = 31 * bits + Float.floatToIntBits(m23);
        bits = 31 * bits + Float.floatToIntBits(m31);
        bits = 31 * bits + Float.floatToIntBits(m32);
        bits = 31 * bits + Float.floatToIntBits(m33);
        return bits;
    }

    /**
     * Returns a string that contains the values of this Matrix3f.
     * @return the String representation
     */
    @Override
    public String toString() {
        return
            "[" + m11 + ", " + m12 + ", " + m13 + "]\n" +
            "[" + m21 + ", " + m22 + ", " + m23 + "]\n" +
            "[" + m31 + ", " + m32 + ", " + m33 + "]";
    }

    // </editor-fold>

} // end class Matrix3f